/**
 * ****************************************************************************
 * sora-editor - the awesome code editor for Android https://github.com/Rosemoe/sora-editor
 * Copyright (C) 2020-2023 Rosemoe
 *
 * This library is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this library;
 * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 * Please contact Rosemoe by email 2073412493@qq.com if you need additional information or have any
 * questions
 * ****************************************************************************
 */
package com.gitee.wsl.eventbus


import com.gitee.wsl.ext.K
import com.gitee.wsl.ext.map.getOrDefault
import com.gitee.wsl.ext.runBlocking
import kotlin.reflect.KClass

class EventEmitter {

    private val listeners = HashMap<String, MutableList<EventListener>>()

    fun addListener(listener: EventListener) {
        listeners.getOrPut(listener.eventName) { ArrayList() }.add(listener)
    }

    fun removeListener(listener: EventListener) {
        listeners[listener.eventName]?.remove(listener)
    }

    fun <T : EventListener> getEventListener(clazz: KClass<T>): T? {
        return listeners.flatMap { it.value }.find { it::class == clazz } as T?
    }

    fun <T : EventListener> removeListener(listenerClass: KClass<T>) {
        listeners.values.forEach { eventListeners ->
            val iterator = eventListeners.iterator()
            iterator.forEach {
                if (it::class == listenerClass) {
                    iterator.remove()
                }
            }
        }
    }

    fun emit(event: String, context: EventContext): EventContext {
        listeners[event]?.forEach { it.handle(context) }
        return context
    }

    suspend fun emitAsync(event: String, context: EventContext): EventContext {
        listeners[event]?.forEach { it.handleAsync(context) }
        return context
    }

    fun emitBlocking(event: String, context: EventContext): EventContext = K.runBlocking {
        emitAsync(event, context)
    }

    fun emit(event: String): EventContext {
        return emit(event, EventContext())
    }

    suspend fun emitAsync(event: String): EventContext {
        return emitAsync(event, EventContext())
    }

    fun emitBlocking(event: String): EventContext = K.runBlocking { emitAsync(event) }

    fun emit(event: String, vararg args: Any): EventContext {
        val context = EventContext()
        for (i in args) {
            context.put(i::class.simpleName!!, i)
        }
        return emit(event, context)
    }

    suspend fun emitAsync(event: String, vararg args: Any): EventContext {
        val context = EventContext()
        for (i in args) {
            context.put(i::class.simpleName!!, i)
        }
        return emitAsync(event, context)
    }


    fun emitBlocking(event: String, vararg args: Any): EventContext = K.runBlocking {
        emitAsync(event, *args)
    }

    fun clear(dispose: Boolean = false) {
        if (dispose) {
            listeners.values.flatten().forEach { eventListeners -> eventListeners.dispose() }
        }
        listeners.clear()
    }
}

interface EventListener {

    val eventName: String

    val isAsync: Boolean
        get() = false

    fun handle(context: EventContext)

    suspend fun handleAsync(context: EventContext) {
        handle(context)
    }

    fun dispose() {}
}

abstract class AsyncEventListener : EventListener {

    override val isAsync = true

    override fun handle(context: EventContext) {
        throw IllegalStateException("This listener is async, please use handleAsync instead")
    }

    override suspend fun handleAsync(context: EventContext) {
        super.handleAsync(context)
    }
}

class EventContext {
    val data = HashMap<String, Any>()

    fun <T : Any> get(key: String): T {
        return data[key] as T
    }

    fun <T : Any> getOrDefault(key: String, default: T): T {
        return data.getOrDefault(key, default) as T
    }

    fun <T : Any> getOrNull(key: String): T? {
        return data[key] as T?
    }

    fun put(key: String, value: Any) {
        data[key] = value
    }

    fun put(value: Any) {
        data[value::class.simpleName!!] = value
    }

    fun <T : Any> remove(key: String): T? {
        return data.remove(key) as T?
    }

    inline fun <reified T> getByClass(): T? {
        return data.values.filterIsInstance<T>().firstOrNull()
    }

    operator fun plus(context: EventContext): EventContext {
        data.putAll(context.data)
        return this
    }

    fun clear() {
        data.clear()
    }

    override fun toString(): String {
        return data.toString()
    }
}

//inline fun <reified T : Any> EventContext.getByClass(): T? {
//    return getByClass(T::class)
//}

inline fun <reified T : Any> EventContext.get(): T {
    return get(T::class.simpleName!!)
}

inline fun <reified T : EventListener> EventEmitter.getEventListener(): T? {
    return getEventListener(T::class) as T?
}

object EventType
